#include "timer_task.h"

#include "timer_channel.h"
#include "timer_description.h"
#include "./../log_module/log_public_module.h"
#include <bits/types/struct_timeval.h>
#include <cstdint>
#include <unistd.h>
#include <ctime>
#include <sys/time.h>
#include <functional>
#include <memory>
#include <sys/timerfd.h>
#include <utility>

TimerTask::TimerTask(Loop * loop)
{
    m_loop = loop;
    m_timer_channel = nullptr;
    m_event_type = 0;
    LOG_TIMER_MODULE("Create TimerTask(%p)", this);
}

TimerTask::~TimerTask()
{
    Cancel();
    LOG_TIMER_MODULE("Release TimerTask(%p)", this);
}

void TimerTask::Cancel()
{
    if (m_timer_channel && m_timer_channel->Index() > 0)
    {
        LOG_TIMER_MODULE("TimerTask(%p) Cancel cancel timer", this);
        m_timer_channel->DelEvent();
        m_event_type = 0;
    }
}

int TimerTask::ExpireAt(int64_t timestamp_ms, TimerFunc && callback)
{
    if (!m_description)
    {
        m_description = std::make_shared<TimerDescription>();
        m_description->CreateDescription();
    }
    if (!m_timer_channel)
    {
        m_timer_channel = std::make_shared<TimerChannel>(m_loop, m_description);
    }
    LOG_TIMER_MODULE("TimerTask(%p) ExpireAt expire time:%ld", this, timestamp_ms);
    struct itimerspec time_value   = {};
    time_value.it_value.tv_sec     = timestamp_ms / 1000;
    time_value.it_value.tv_nsec    = (timestamp_ms % 1000) * 1000;
    time_value.it_interval.tv_sec  = 0;
    time_value.it_interval.tv_nsec = 0;
    int result = timerfd_settime(m_description->DescriptionData(), TFD_TIMER_ABSTIME, &time_value, nullptr);
    if (result >= 0)
    {
        result = 1;
        m_timer_channel->Trigger([callback, this](const std::shared_ptr<ChannelProtocol>& channel) {
            uint64_t value = {};
            read(channel->DescriptionData(), &value, sizeof(value));
            LOG_TIMER_MODULE("TimerTask(%p) Trigger trigger timer", this);
            callback();
        });
        if (m_event_type == 0)
        {
            m_event_type = EmIoIn | EmIoOneShot;
            m_timer_channel->AddEvent(m_event_type);
        }
        else
        {
            m_timer_channel->ModEvent(m_event_type);
        }
    }
    else
    {
        result = 0;
    }
    return result;
}

int TimerTask::ExpireFromNow(const int ms, TimerFunc && callback)
{
    struct timeval tm = {};
    gettimeofday(&tm, nullptr);
    int result = ExpireAt(ms + tm.tv_sec * 1000 + tm.tv_usec / 1000, std::forward<std::function<void()>>(callback));
    return result;
}
